package net.orange.console.model.analyzer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import net.orange.console.model.dataset.ConsoleData;
import net.orange.console.model.dataset.ConsoleDataSet;
import net.orange.console.model.dataset.ConsoleMetaData;
import net.orange.console.model.dataset.ConsoleRow;

/**
 * The dimension definition of the AnalyseTable, it is the iterative data
 * structure and contain the row data set.
 * 
 */
public class AnalyseDimension {

	public final static String KEY_KEY_SEPARATOR = "!!";

	public final static String DIM_DIM_SEPARATOR = "@@";
	/**
	 * The console meta data of the this analyze column.
	 */
	private ConsoleMetaData metaData;

	/**
	 * The parent AnalyseColumn.
	 */
	private AnalyseDimension parentAnalyseDimension;

	/**
	 * The dimension data of the AnalyseTable.
	 */
	private Map<String, Collection<ConsoleRow>> datas = new HashMap<String, Collection<ConsoleRow>>();

	/**
	 * The constructor with the console meta data and initialize console data
	 * set , it may use for first level constructor.
	 * 
	 * @param metaData
	 *            The target field meta data.
	 * @param dataSet
	 *            The initialize console data set
	 */
	public AnalyseDimension(ConsoleMetaData metaData, ConsoleDataSet dataSet) {
		this.metaData = metaData;
		setRootDatas(metaData, dataSet);
	}

	/**
	 * The constructor with the console meta data and parentAnalyseRow , it may
	 * use for non-first level constructor.
	 * 
	 * @param metaData
	 *            The target field meta data.
	 * @param parentAnalyseRow
	 *            The parent AnalyseRow
	 */
	public AnalyseDimension(ConsoleMetaData metaData,
			AnalyseDimension parentAnalyseRow) {

		this.metaData = metaData;
		this.parentAnalyseDimension = parentAnalyseRow;

		Map<String, Collection<ConsoleRow>> parentDatas = parentAnalyseRow
				.getDatas();

		for (String parentKey : parentDatas.keySet()) {
			for (ConsoleRow currentRow : parentDatas.get(parentKey)) {

				String key = parentKey + KEY_KEY_SEPARATOR
						+ currentRow.getData(metaData).getStringValue();

				if (datas.get(key) == null) {
					Collection<ConsoleRow> rows = new ArrayList<ConsoleRow>();

					rows.add(currentRow);

					datas.put(key, rows);
				} else {
					datas.get(key).add(currentRow);
				}
			}
		}
	}

	/**
	 * Initialize the first level console data set.
	 * 
	 * @param metaData
	 *            The target field meta data.
	 * @param dataSet
	 *            The initialize console data set.
	 */
	public void setRootDatas(ConsoleMetaData metaData, ConsoleDataSet dataSet) {

		for (ConsoleRow currentRow : dataSet.getRows()) {

			ConsoleData consoleData = currentRow.getData(metaData);

			String key = (consoleData != null && consoleData.getStringValue() != null) ? consoleData
					.getStringValue()
					: "";

			if (datas.get(key) == null) {
				Collection<ConsoleRow> rows = new ArrayList<ConsoleRow>();

				rows.add(currentRow);

				datas.put(key, rows);
			} else {
				datas.get(key).add(currentRow);
			}
		}
	}

	/**
	 * @return the metaData
	 */
	public ConsoleMetaData getMetaData() {
		return metaData;
	}

	/**
	 * @param metaData
	 *            the metaData to set
	 */
	public void setMetaData(ConsoleMetaData metaData) {
		this.metaData = metaData;
	}

	/**
	 * @return the parentAnalyseRow
	 */
	public AnalyseDimension getParentAnalyseDimension() {
		return parentAnalyseDimension;
	}

	/**
	 * @param parentAnalyseRow
	 *            the parentAnalyseRow to set
	 */
	public void setParentAnalyseDimension(AnalyseDimension parentAnalyseRow) {
		this.parentAnalyseDimension = parentAnalyseRow;
	}

	/**
	 * @return the datas
	 */
	public Map<String, Collection<ConsoleRow>> getDatas() {
		return datas;
	}

	/**
	 * Return the datas key which is in order.
	 * 
	 * @return The datas key which is in order.
	 */
	public List<String> getDatasKeyIterator() {

		List<AnalyseDataKeyPair> list = new ArrayList();

		Collection<AnalyseDimension> hierarchy = getHierarchy();

		for (String key : datas.keySet()) {
			AnalyseDataKeyPair pair = new AnalyseDataKeyPair(key, hierarchy);
			list.add(pair);
		}

		Collections.sort(list, new AnalyseDataKeyComparator());

		List<String> keyList = new ArrayList();

		for (AnalyseDataKeyPair pair : list) {
			keyList.add(pair.getKey());
		}

		return keyList;
	}

	/**
	 * @param datas
	 *            the datas to set
	 */
	public void setDatas(Map<String, Collection<ConsoleRow>> datas) {
		this.datas = datas;
	}

	/**
	 * Get the data divide by the another AnalyseDimension.
	 * 
	 * @param targetDimesion
	 *            The target dimension
	 * @return The cross data.
	 */
	public Map<String, Collection<ConsoleRow>> getCrossDatas(
			AnalyseDimension targetDimesion) {

		Map<String, Collection<ConsoleRow>> rowDataMap = getDatas();

		Map<String, Collection<ConsoleRow>> columntDataMap = targetDimesion
				.getDatas();

		Map<String, Collection<ConsoleRow>> resultData = new HashMap<String, Collection<ConsoleRow>>();

		// Search all the row data.
		for (String rowKey : rowDataMap.keySet()) {

			Collection<ConsoleRow> rowData = rowDataMap.get(rowKey);

			for (ConsoleRow rowConsoleRow : rowData) {

				// Search all the column data
				for (String columnKey : columntDataMap.keySet()) {

					Collection<ConsoleRow> columntData = columntDataMap
							.get(columnKey);

					for (ConsoleRow columnConsoleRow : columntData) {

						// If the row data match the column data.
						if (rowConsoleRow.getPrimaryKey() == columnConsoleRow
								.getPrimaryKey()) {

							String newKey = rowKey + DIM_DIM_SEPARATOR
									+ columnKey;

							if (resultData.get(newKey) == null) {
								Collection<ConsoleRow> resultRows = new ArrayList<ConsoleRow>();

								resultRows.add(rowConsoleRow);

								resultData.put(newKey, resultRows);
							} else {
								resultData.get(newKey).add(rowConsoleRow);
							}
						}
					}
				}
			}
		}
		return resultData;
	}

	/**
	 * Get the analyse dimension hierarchy.
	 * 
	 * @return The analyse dimension hierarchy array.
	 */
	private Collection<AnalyseDimension> getHierarchy() {
		Collection hierarchy = new Stack();

		hierarchy.add(this);

		AnalyseDimension parent = parentAnalyseDimension;

		while (parent != null) {
			hierarchy.add(parent);
			parent = parent.getParentAnalyseDimension();
		}

		return hierarchy;
	}
}
